﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EzDesk.Client.Utility;
using System.Windows.Threading;
using EzDesk.DesktopClient.ServiceLocator.ClientService;
using EzDesk.Generic;
using EzDesk.DesktopClient.Model;
using EzDesk.DesktopClient;
using System.Threading;
using EzDesk.DesktopClient.Event;
using EzDesk.EventAggregation;
using System.Collections.ObjectModel;
using System.Diagnostics;
using EzDesk.Generic.Collections;
using DbLinq;
using DbLinq.Sqlite;
using System.Data.SQLite;

namespace EzDesk.DesktopClient
{

    public enum FeedType
    {
        update,
        notification
    }

    /// <summary>
    /// @Refactory 
    /// This class should be split into 2 classes.
    /// </summary>
    public class DataManagement : ManagementBase<DataManagement>
    {

        private const string FEED_STORAGE_KEY = "FeedData";
        private const string LAST_LOADDATE_STORAGE_KEY = "lastLoadDatetime";
        private const string FEED_CLIENT_TYPE = "xaml";
        private DateTime MIN_DATE = new DateTime(1900, 1, 1);
        private const string FEED_TYPE_NOTIFICATION = "notification";
        private const string FEED_TYPE_UPDATE = "update";


        private DispatcherTimer _disappearTimer = new DispatcherTimer();
        private TimeSpan DurationTime = TimeSpan.FromSeconds(8);
        private bool _isRunning = false;

        private DateTime _lastLoadDatetime = new DateTime(1900, 1, 1);
        private EzDesktopClientDataContext _clientDBContext;


        public int NotificationDisplayCount
        {
            get
            {
                return ClientConfigurationManagement.Instance.GetSetting<int>("NotificationDisplayCount");
            }
        }
        public int UpdateDisplayCount
        {
            get
            {
                return ClientConfigurationManagement.Instance.GetSetting<int>("UpdateDisplayCount");
            }
        }

        public UserProfile User
        {
            get
            {
                return UserManagement.Instance.LogUser;
            }
        }

        public DateTime LastLoadDateTime
        {
            get
            {
                //Judge whether the first time load feed when application start up
                if (_lastLoadDatetime == MIN_DATE)
                {
                    _lastLoadDatetime = this.Storage.Load<DateTime>(User.UserName, LAST_LOADDATE_STORAGE_KEY);
                    //Judge wheter the first time run this application for the login user
                    if (_lastLoadDatetime == DateTime.MinValue) _lastLoadDatetime = MIN_DATE;
                }
                return _lastLoadDatetime;
            }
            set
            {
                _lastLoadDatetime = LastLoadDateTime;
            }
        }

        private void SetDuration()
        {
            _disappearTimer.Interval = DurationTime;
        }
        /// <summary>
        /// @Refactory This function is too loog.
        /// </summary>
        private void GetDataFromLocal()
        {
            this.PublishDataUpdateEvent();
        }

        private void PublishDataUpdateEvent()
        {
            FeedUpdateArgs dataUpdateArgs = new FeedUpdateArgs()
            {
                NotificationCollection = this.GetFeed(0, NotificationDisplayCount, FEED_TYPE_NOTIFICATION),
                UpdateCollection = this.GetFeed(0, UpdateDisplayCount, FEED_TYPE_UPDATE)
            };

            FeedUpdateEvent.Instance.Publish(dataUpdateArgs);
        }

        private void GetDataFromServer()
        {
            DateTime toDateTime = DateTime.Now;
            bool hasNotification = this.GetUpdateFromServer(toDateTime);
            bool hasUpdate = this.GetNotificationFromServer(toDateTime);

            //Trigger event when has notfication or update
            (hasNotification || hasUpdate).TrueDo(this.PublishDataUpdateEvent);

            this.LastLoadDateTime = toDateTime;
        }
        /// @Refactory This function is too loog.
        private bool GetUpdateFromServer(DateTime toDatetime)
        {
            string updateFeedString = ClientService.GetXmlFeedsBetweenDate(FeedType.update.GetEnumName(), User.Id, this.LastLoadDateTime, toDatetime, null, null);
            FeedCollection updateCollection = FeedCollection.Create(updateFeedString, FEED_CLIENT_TYPE);
            if (updateCollection.Count == 0) return false;

            this._clientDBContext.Feed.InsertAllOnSubmit(updateCollection.Select(n => new Feed(n)));
            this._clientDBContext.SubmitChanges();
            return true;
        }

        private bool GetNotificationFromServer(DateTime toDateTime)
        {
            UserProfile user = UserManagement.Instance.LogUser;
            string notificationFeedString = ClientService.GetXmlFeedsBetweenDate(FeedType.notification.GetEnumName(), User.Id, this.LastLoadDateTime, toDateTime, null, null);
            FeedCollection notificationCollection = FeedCollection.Create(notificationFeedString, FEED_CLIENT_TYPE);
            if (notificationCollection.Count == 0) return false;

            this._clientDBContext.Feed
                .InsertAllOnSubmit(notificationCollection.Select(n => new Feed(n)));
            this._clientDBContext.SubmitChanges();
            return true;
        }

        public void Start()
        {
            _isRunning = true;
            _disappearTimer.Start();
        }

        public void Start(int duration)
        {
            DurationTime = TimeSpan.FromSeconds(duration);
            SetDuration();
            this.Start();
        }

        public void Stop()
        {
            if (_isRunning)
            {
                _disappearTimer.Stop();
                this.Storage.Save(_lastLoadDatetime, UserManagement.Instance.LogUser.UserName, LAST_LOADDATE_STORAGE_KEY);
                this._clientDBContext.SubmitChanges();
            }
        }

        public DataManagement()
            : base()
        {
            InitField();
            InitTickEvent();
            InitEvent();
        }

        private void InitField()
        {
            Debug.Write("superwolf");
            _clientDBContext = new EzDesktopClientDataContext(new SQLiteConnection(ClientDb.GetConnectionString(this.CurrentUser.Name)));
            Debug.Write("superwolf");
        }


        private void InitTickEvent()
        {
            this.GetDataFromLocal();
            _disappearTimer.Tick += (sender, e) =>
            {
                this.GetDataFromServer();
            };
            SetDuration();
            Start();
        }

        private void InitEvent()
        {
            LogoutEvent.Instance.Subscribe(this.OnUserLogout, ThreadOption.BackgroundThread);
        }


        private void OnUserLogout(LogoutArgs args)
        {
            this.Stop();
        }

        public IList<Feed> GetFeed(int startIndex, int count, string type)
        {

            return this._clientDBContext.Feed
                            .Where(feed => feed.Type == type)
                            .OrderByDescending(feed => feed.UpdateOn)
                            .Take(count)
                           .ToList();
        }
    }
}
